//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_SUB_FILE_H
#define _UXS_SUB_FILE_H

#pragma once

#include "BaseFile.h"

namespace UX_STL
{
namespace File
{


	//
	// Read only Sub File
	//

	class SubRFile : public RFile
	{
	// types
	public:
		SHARED_POINTER( SubRFile );

		typedef SubRFile	Self;


	// variables
	protected:
		RFilePtr	_file;
		isize		_offset,
					_size,
					_pos;


	// methods
	public:
		SubRFile (const File::RFilePtr &file, usize offset, usize size):
			_file(file), _offset(offset), _size(size), _pos(0)
		{
			ASSERT( _file.IsNotNull() );
			ASSERT( offset + size <= _file->Size() );
		}


		static SubRFilePtr New (const File::RFilePtr &file, usize offset, usize size)
		{
			return new Self( file, offset, size );
		}
		

		// RFile //
		virtual isize ReadBuf (void * buf, usize size) override
		{
			const usize	pos		= _file->Pos();
			const usize	new_pos = _offset + _pos;

			if ( pos != new_pos )
				_file->SeekSet( new_pos );

			size = _file->ReadBuf( buf, size );
			_pos += (isize)size;

			_file->SeekSet( pos );
			return size;
		}
		

		// BaseFile //
		virtual void Close () override
		{
			_file	= null;
			_offset	= 0;
			_size	= 0;
			_pos	= 0;
		}
		
		virtual bool IsOpened () override
		{
			return _file.IsNotNull();
		}

		virtual bool SeekSet (usize offset) override
		{
			return _SetPos( _offset + offset );
		}

		virtual bool SeekCur (isize offset) override
		{
			return _SetPos( _pos + offset );
		}

		virtual bool SeekEnd (usize offset) override
		{
			return _SetPos( _offset + _size - offset );
		}
		
		virtual usize RemainingSize () const override
		{
			return _size - _pos;
		}

		virtual usize Size () const override
		{
			return _size;
		}

		virtual usize Pos () const override
		{
			return _pos;
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return _file->Name();
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::SUB_FILE;
		}


	private:
		bool _SetPos (isize newPos)
		{
			if ( newPos >= _offset and
				 newPos < _offset + _size )
			{
				_pos = newPos;
				return true;
			}
			else
			{
				_pos = 0;
				return false;
			}
		}
	};



	//
	// Write only Sub File
	//

	class SubWFile : public WFile
	{
	// types
	public:
		SHARED_POINTER( SubWFile );

		typedef SubWFile	Self;


	// variables
	protected:
		WFilePtr	_file;
		isize		_offset,
					_size,
					_pos;


	// methods
	public:
		SubWFile (const File::WFilePtr &file, usize offset, usize size):
			_file(file), _offset(offset), _size(size), _pos(0)
		{
			ASSERT( _file.IsNotNull() );
			ASSERT( offset + size <= _file->Size() );
		}


		static SubWFilePtr New (const File::WFilePtr &file, usize offset, usize size)
		{
			return new Self( file, offset, size );
		}
		

		// WFile //
		virtual isize WriteBuf (const void * buf, usize size) override
		{
			const usize	pos		= _file->Pos();
			const usize	new_pos = _offset + _pos;

			if ( pos != new_pos )
				_file->SeekSet( new_pos );

			size = _file->WriteBuf( buf, size );
			_pos += (isize)size;

			_file->SeekSet( pos );
			return size;
		}
		
		virtual void Flush () override
		{
			_file->Flush();
		}


		// BaseFile //
		virtual void Close () override
		{
			_file	= null;
			_offset	= 0;
			_size	= 0;
			_pos	= 0;
		}
		
		virtual bool IsOpened () override
		{
			return _file.IsNotNull();
		}
		
		virtual bool SeekSet (usize offset) override
		{
			return _SetPos( _offset + offset );
		}

		virtual bool SeekCur (isize offset) override
		{
			return _SetPos( _pos + offset );
		}

		virtual bool SeekEnd (usize offset) override
		{
			return _SetPos( _offset + _size - offset );
		}
		
		virtual usize RemainingSize () const override
		{
			return _size - _pos;
		}

		virtual usize Size () const override
		{
			return _size;
		}

		virtual usize Pos () const override
		{
			return _pos;
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return _file->Name();
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::SUB_FILE;
		}


	private:
		bool _SetPos (isize newPos)
		{
			if ( newPos >= _offset &&
				 newPos < _offset + _size )
			{
				_pos = newPos;
				return true;
			}
			else
			{
				_pos = 0;
				return false;
			}
		}
	};
	

}	// File


namespace UXTypes
{
	typedef SHARED_POINTER_TYPE( File::SubRFile )	SubRFile;
	typedef SHARED_POINTER_TYPE( File::SubWFile )	SubWFile;

}	// UXTypes
}	// UX_STL

#endif	// _UXS_SUB_FILE_H